#include <stdio.h>
#include "iuModuleARP.hpp"
#include "iuPacketARP.hpp"
#include "iuInterface.hpp"
#include "iuIPAddress.hpp"
#include "iuMacAddress.hpp"

/**
 * @brief Initialise le module ARP
 */
iuModuleARP::iuModuleARP()
{
   m_onReplyCallback = NULL;
   m_onReplyData = NULL;
}

/**
 * @brief Fonction appelee lorsque l'application recoit un paquet ARP. Cette fonction
 * va s'occuper de mettre le cache ARP a jour et de repondre aux requetes ARP.
 * @param packet le paquet ARP recu
 * @param in l'interface ayant recu le paquest
 */
void iuModuleARP::receivePacket(iuPacketARP &packet, iuInterface &in)
{
   //ARP-Request: Envoie d'une reponse si le paquet nous est addresse
   if (packet.getType() == ARP_REQUEST)
   {
      if (packet.getDestinationIp() == in.getIP())
      {
         iuPacketARP reply;

         reply.setType(ARP_REPLY);
         reply.setDestinationMac(packet.getSourceMac());
         reply.setSourceMac(in.getMAC());
         reply.setDestinationIp(packet.getSourceIp());
         reply.setSourceIp(in.getIP());

         in.sendPacket(reply, reply.getDestinationMac(), 0x806);
      }
   }

   //ARP-Reply: Ajout de la correspondance IP/MAC dans le cache ARP
   if (packet.getType() == ARP_REPLY)
   {
      iuARPEntry *entry = new iuARPEntry();

      entry->interface = &in;
      entry->mac = packet.getSourceMac();
      entry->ip = packet.getSourceIp();

      m_table.addEntry(entry);

      if (m_onReplyCallback)
         m_onReplyCallback(*entry, m_onReplyData);
   }
}

/**
 * @brief Demande au module ARP d'envoyer une requete
 * a l'IP donnee par l'interface passee en parametre.
 * @param ip l'adresse de destination a resoudre
 * @param in l'interface par laquelle il faut envoyer le paquet
 */
void iuModuleARP::sendRequest(iuIPAddress ip, iuInterface &in)
{
   iuPacketARP request;
   iuMacAddress destMac;

   request.setType(ARP_REQUEST);
   request.setSourceMac(in.getMAC());
   request.setDestinationIp(ip);
   request.setSourceIp(in.getIP());

   destMac.setToBroadcast();
   in.sendPacket(request, destMac, 0x806);
}

/**
 * @brief Definit la fonction a appeler lorsqu'une reponse ARP est recu par le module
 * @param callback la fonction a appeler
 * @param data le derniere argument a passer a la fonction a appeler
 */
void iuModuleARP::setOnReplyCallback(void (*callback)(const iuARPEntry &entry, void *data), void *data)
{
   m_onReplyCallback = callback;
   m_onReplyData = data;
}

/**
 * @brief Recupere la table ARP
 * @return Renvoie la table ARP.
 */
iuARPTable &iuModuleARP::getARPTable()
{
   return m_table;
}
